package com.huffman.smart_picture.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.lang.TypeReference;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.huffman.smart_picture.api.download.DownloadImageFactory;
import com.huffman.smart_picture.api.download.ObtainImageQueryDto;
import com.huffman.smart_picture.api.download.ObtainImageResult;
import com.huffman.smart_picture.api.download.pexels.PexelsImageQueryDto;
import com.huffman.smart_picture.api.process.PictureProcessApi;
import com.huffman.smart_picture.api.process.ProcessResult;
import com.huffman.smart_picture.api.process.dto.BackgroundProcessDto;
import com.huffman.smart_picture.api.process.dto.IdPhotoProcessDto;
import com.huffman.smart_picture.api.process.dto.WatermarkProcessDto;
import com.huffman.smart_picture.common.UserSession;
import com.huffman.smart_picture.common.base.reponse.PageQueryResult;
import com.huffman.smart_picture.common.exception.BusinessException;
import com.huffman.smart_picture.common.exception.ErrorEnum;
import com.huffman.smart_picture.domain.constants.RedisConstant;
import com.huffman.smart_picture.domain.dto.picture.WaterfallQueryDto;
import com.huffman.smart_picture.domain.entity.Picture;
import com.huffman.smart_picture.domain.entity.Space;
import com.huffman.smart_picture.domain.enums.PictureCategoryEnum;
import com.huffman.smart_picture.domain.enums.PictureReviewStatusEnum;
import com.huffman.smart_picture.domain.request.picture.*;
import com.huffman.smart_picture.domain.vo.pircture.*;
import com.huffman.smart_picture.manager.FilePictureUpload;
import com.huffman.smart_picture.manager.PictureUploadDto;
import com.huffman.smart_picture.manager.PictureUploadTemplate;
import com.huffman.smart_picture.manager.UrlPictureUpload;
import com.huffman.smart_picture.mapper.PictureMapper;
import com.huffman.smart_picture.service.FavoritePictureRecordService;
import com.huffman.smart_picture.service.PictureService;
import com.huffman.smart_picture.service.SpaceService;
import com.huffman.smart_picture.utils.ColorUtil;
import com.huffman.smart_picture.utils.ThrowUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.support.TransactionTemplate;
import org.springframework.util.DigestUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;

import java.awt.*;
import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

@Slf4j
@Service
@RequiredArgsConstructor
public class PictureServiceImpl extends ServiceImpl<PictureMapper, Picture> implements PictureService {
    //    private static final Logger log = LoggerFactory.getLogger(PictureServiceImpl.class);
    private final SpaceService spaceService;
    private final FavoritePictureRecordService favoritePictureByLuaService;
    private final FilePictureUpload filePictureUpload;
    private final UrlPictureUpload urlPictureUpload;
    private final TransactionTemplate transactionTemplate;
    private final StringRedisTemplate stringRedisTemplate;
    private final DownloadImageFactory downloadImageFactory;
    private final PictureProcessApi pictureProcessApi;

    private final Cache<String, String> LOCAL_CACHE =
            Caffeine.newBuilder().initialCapacity(1024)
                    .maximumSize(10000L)
                    // 缓存 5 分钟移除
                    .expireAfterWrite(5L, TimeUnit.MINUTES)
                    .build();


    public PictureVo uploadPicture(Object inputSource, PictureUploadRequest pictureUploadRequest, UserSession userSession) {
        ThrowUtil.throwIf(userSession == null, ErrorEnum.NO_AUTH, "未登录没权限");
        Long pictureId = null;
        if (pictureUploadRequest.getId() != null) {
            pictureId = pictureUploadRequest.getId();
        }

        Long spaceId = pictureUploadRequest.getSpaceId();
        if (spaceId != null) {
            Space space = this.spaceService.getById(spaceId);
            ThrowUtil.throwIf(space == null, ErrorEnum.RESOURCE_NOT_FIND, "空间不存在");
            if (!space.getUserId().equals(userSession.getId()) && !userSession.isAdmin()) {
                throw new BusinessException(ErrorEnum.NO_AUTH, "没有空间权限");
            }

            if (space.getTotalCount() >= space.getMaxCount()) {
                throw new BusinessException(ErrorEnum.NO_AUTH, "空间条数不足");
            }

            if (space.getTotalSize() >= space.getMaxSize()) {
                throw new BusinessException(ErrorEnum.NO_AUTH, "空间大小不足");
            }
        }

        if (pictureId != null) {
            Picture oldPicture = this.getById(pictureId);
            ThrowUtil.throwIf(oldPicture == null, ErrorEnum.RESOURCE_NOT_FIND, "当前图片不存在");
            if (!userSession.isAdmin() && !oldPicture.getUserId().equals(userSession.getId())) {
                throw new BusinessException(ErrorEnum.NO_AUTH, "没有权限修改当前图片");
            }

            Long oldPictureSpaceId = oldPicture.getSpaceId();
            if (spaceId == null) {
                if (oldPictureSpaceId != null) {
                    spaceId = oldPictureSpaceId;
                }
            } else {
                ThrowUtil.throwIf(!spaceId.equals(oldPictureSpaceId), ErrorEnum.PARAM_CHECK_MISTAKE, "空间id不一致");
            }
        }

        Long userId = userSession.getId();
        String uploadPrefix;
        if (spaceId == null) {
            uploadPrefix = String.format("public/%s", userSession.getId());
        } else {
            uploadPrefix = String.format("space/%s", spaceId);
        }

        PictureUploadTemplate pictureUploadTemplate = this.filePictureUpload;
        if (inputSource instanceof String) {
            pictureUploadTemplate = this.urlPictureUpload;
        }

        PictureUploadDto pictureUploadDto = pictureUploadTemplate.uploadPicture(inputSource, uploadPrefix, userSession.isAdmin());
        Picture picture = new Picture();
        picture.setUrl(pictureUploadDto.getUrl());
        picture.setPicColor(pictureUploadDto.getPicColor());
        picture.setPicSize(pictureUploadDto.getPicSize());
        picture.setPicWidth(pictureUploadDto.getPicWidth());
        picture.setPicHeight(pictureUploadDto.getPicHeight());
        picture.setPicScale(pictureUploadDto.getPicScale());
        picture.setPicFormat(pictureUploadDto.getPicFormat());
        picture.setThumbnailUrl(pictureUploadDto.getThumbnailUrl());
        picture.setCompressUrl(pictureUploadDto.getCompressUrl());
        picture.setUserId(userId);
        picture.setSpaceId(spaceId);

        picture.setIntroduction(pictureUploadRequest.getIntroduction());
        picture.setPicName(pictureUploadRequest.getPicName());
        picture.setCategory(pictureUploadRequest.getCategory());
        picture.setTags(JSONUtil.toJsonStr(pictureUploadRequest.getTags()));
        if (userSession.isAdmin()) {
            picture.setReviewStatus(PictureReviewStatusEnum.PASS.getValue());
            picture.setReviewerId(userSession.getId());
            picture.setReviewMessage("管理员导入");
            picture.setReviewTime(new Date());
        }

        if (spaceId != null && pictureId == null) {
            picture.setReviewStatus(PictureReviewStatusEnum.PASS.getValue());
            picture.setReviewerId(0L);
            picture.setReviewMessage("用户空间图片 自动过审");
            picture.setReviewTime(new Date());
        }

        if (pictureId != null) {
            picture.setId(pictureId);
            picture.setEditTime(new Date());
        }

        Long finalSpaceId = spaceId;
        this.transactionTemplate.execute((status) -> {
            boolean result = this.saveOrUpdate(picture);
            ThrowUtil.throwIf(!result, ErrorEnum.SYSTEM_ERROR, "系统错误,图片上传失败");
            if (finalSpaceId != null) {
                boolean update = this.spaceService.lambdaUpdate().eq(Space::getId, finalSpaceId)
                        .setSql("total_size = total_size + " + picture.getPicSize())
                        .setSql("total_count = total_count + 1")
                        .update();
                ThrowUtil.throwIf(!update, ErrorEnum.SYSTEM_ERROR, "系统错误,额度更新失败");
            }
            return picture;
        });
        return PictureVo.objToVo(picture);
    }

    public void saveOrUpdatePicture(PictureUpdateRequest pictureUpdateRequest, UserSession userSession) {
        Picture picture = PictureUpdateRequest.convert(pictureUpdateRequest);
        if (picture.getId() != null) {
            Picture oldPicture = this.getById(picture.getId());
            ThrowUtil.throwIf(oldPicture == null, ErrorEnum.RESOURCE_NOT_FIND, "当前图片不存在");
        }

        boolean condition = userSession.isAdmin() || picture.getUserId().equals(userSession.getId());
        ThrowUtil.throwIf(!condition, ErrorEnum.NO_AUTH, "删除失败，权限不足");
        if (userSession.isAdmin()) {
            picture.setReviewerId(userSession.getId());
            picture.setReviewMessage("管理员上传自动过审");
            picture.setReviewStatus(PictureReviewStatusEnum.PASS.getValue());
            picture.setReviewTime(new Date());
        } else {
            picture.setReviewStatus(PictureReviewStatusEnum.REVIEWING.getValue());
        }

        picture.setEditTime(new Date());
        boolean updateRes = this.updateById(picture);
        ThrowUtil.throwIf(!updateRes, ErrorEnum.SYSTEM_ERROR, "上传更新失败");
    }

    public PageQueryResult<PictureViewVo> pageQueryByAdmin(PicturePageQueryRequest picturePageQueryRequest) {
        QueryWrapper<Picture> queryWrapper = this.buildQueryWrapper(picturePageQueryRequest);
        queryWrapper.isNull("space_id");
        Integer pageNum = picturePageQueryRequest.getPageNum();
        Integer pageSize = picturePageQueryRequest.getPageSize();
        Page<Picture> page = this.page(pageNum, pageSize, queryWrapper);
        List<PictureViewVo> pictureViewVos = page.getRecords()
                .stream()
                .map(PictureViewVo::convert)
                .collect(Collectors.toList());
        PageQueryResult<PictureViewVo> pageQueryResult = new PageQueryResult();
        pageQueryResult.setData(pictureViewVos);
        pageQueryResult.setPages(page.getPages());
        pageQueryResult.setTotal(page.getTotal());
        return pageQueryResult;
    }

    @Override
    public PageQueryResult<WaterfallPictureVo> pageQueryWaterfall(PicturePageQueryRequest picturePageQueryRequest, UserSession userSession) {
        // 构建缓存 查询条件
        String queryCondition = JSONUtil.toJsonStr(picturePageQueryRequest);
        String hashKey = DigestUtils.md5DigestAsHex(queryCondition.getBytes());
        String cacheKey = RedisConstant.PICTURE_VO_PAGE_QUERY_BY_USER + ":" + hashKey;
        List<WaterfallPictureVo> waterfallPictureVos = null;
        // 从本地缓存中查询
        String cacheValue = LOCAL_CACHE.getIfPresent(cacheKey);
        if (cacheValue != null) {
            PageQueryResult<WaterfallPictureVo> cacheQueryResult = JSONUtil.toBean(cacheValue, new TypeReference<PageQueryResult<WaterfallPictureVo>>() {
            }, false);
            // 填充是否点赞字段
            waterfallPictureVos = fillWaterfallPictureVoField(userSession, cacheQueryResult.getData());
            cacheQueryResult.setData(waterfallPictureVos);
            return cacheQueryResult;
        }
        // 本地缓存不在 从redis 中查询
        String redisKey = RedisConstant.MODULE_NAME + ":" + cacheKey;
        ValueOperations<String, String> opsForValue = stringRedisTemplate.opsForValue();
        cacheValue = opsForValue.get(redisKey);
        // redis 命中
        if (cacheValue != null) {
            PageQueryResult<WaterfallPictureVo> cacheQueryResult = JSONUtil.toBean(cacheValue, new TypeReference<PageQueryResult<WaterfallPictureVo>>() {
            }, false);
            //存本地缓存
            LOCAL_CACHE.put(cacheKey, JSONUtil.toJsonStr(cacheQueryResult));
            // 填充是否点赞字段
            waterfallPictureVos = fillWaterfallPictureVoField(userSession, cacheQueryResult.getData());
            cacheQueryResult.setData(waterfallPictureVos);
            return cacheQueryResult;
        }
        // redis 未命中分页查数据库
        // 构建查询条件
        WaterfallQueryDto waterfallQueryDto = new WaterfallQueryDto();
        waterfallQueryDto.setPicName(picturePageQueryRequest.getSearchText());
        waterfallQueryDto.setIntroduction(picturePageQueryRequest.getSearchText());
        waterfallQueryDto.setCategory(picturePageQueryRequest.getCategory());
        waterfallQueryDto.setTags(picturePageQueryRequest.getTags());
        // 分页查询
        IPage<WaterfallPictureVo> pageQuery = new Page<>();
        pageQuery.setCurrent(picturePageQueryRequest.getPageNum());
        pageQuery.setSize(picturePageQueryRequest.getPageSize());
        Page<WaterfallPictureVo> pageQueryResult = getBaseMapper().pageQueryPictureVos(waterfallQueryDto, pageQuery);

        // 封装查询结果
        PageQueryResult<WaterfallPictureVo> pageQueryResultVo = new PageQueryResult<>();
        pageQueryResultVo.setData(pageQueryResult.getRecords());
        pageQueryResultVo.setPages(pageQueryResult.getPages());
        pageQueryResultVo.setTotal(pageQueryResult.getTotal());
        String cacheResult = JSONUtil.toJsonStr(pageQueryResultVo);
        // 存到本地缓存
        LOCAL_CACHE.put(cacheKey, cacheResult);
        // 存到redis 随机过期时间5-10分钟
        long expireTime = 5 * RedisConstant.ONE_MINUTE + RandomUtil.randomLong(RedisConstant.ONE_MINUTE, 5 * RedisConstant.ONE_MINUTE);
        opsForValue.set(redisKey, cacheResult, expireTime, TimeUnit.MILLISECONDS);

        // 填充是否点赞字段
        waterfallPictureVos = fillWaterfallPictureVoField(userSession, pageQueryResult.getRecords());
        pageQueryResultVo.setData(waterfallPictureVos);
        return pageQueryResultVo;
    }


    public PageQueryResult<Picture> pageQuerySpacePicture(PicturePageQueryRequest picturePageQueryRequest, UserSession userSession) {
        Long spaceId = picturePageQueryRequest.getSpaceId();
        Space space = this.spaceService.getById(spaceId);
        ThrowUtil.throwIf(!space.getUserId().equals(userSession.getId()), ErrorEnum.NO_AUTH, "查询失败,权限不足");
        QueryWrapper<Picture> queryWrapper = this.buildQueryWrapper(picturePageQueryRequest);
        Integer pageNum = picturePageQueryRequest.getPageNum();
        Integer pageSize = picturePageQueryRequest.getPageSize();
        Page<Picture> page = this.page(pageNum, pageSize, queryWrapper);
        PageQueryResult<Picture> pageQueryResult = new PageQueryResult();
        pageQueryResult.setData(page.getRecords());
        pageQueryResult.setPages(page.getPages());
        pageQueryResult.setTotal(page.getTotal());
        return pageQueryResult;
    }

    public Picture getPictureById(Long id) {
        return null;
    }

    public PictureDetailVo getPictureDetail(Long id, UserSession userSession) {
        PictureDetailVo pictureDetailVo = getBaseMapper().queryPictureDetailVo(id);
        ThrowUtil.throwIf(pictureDetailVo == null, ErrorEnum.RESOURCE_NOT_FIND, "图片不存在");
        // 填充当前用户是否点赞
        pictureDetailVo.setIsFavorite(fillIsFavoriteField(id, userSession));
        return pictureDetailVo;
    }

    public boolean deletePicture(Long pictureId, UserSession userSession) {
        Picture picture = this.getById(pictureId);
        ThrowUtil.throwIf(picture == null, ErrorEnum.RESOURCE_NOT_FIND, "当前图片不存在");
        this.checkPictureAuth(userSession, picture);
        this.transactionTemplate.execute((status) -> {
            boolean result = this.removeById(pictureId);
            ThrowUtil.throwIf(!result, ErrorEnum.SYSTEM_ERROR, "系统错误，删除失败");
            Long spaceId = picture.getSpaceId();
            if (spaceId != null) {
                // 修改存储空间大小
                boolean update = this.spaceService.lambdaUpdate().eq(Space::getId, spaceId)
                        .setSql("total_size = total_size - " + picture.getPicSize())
                        .setSql("total_count = total_count - 1")
                        .update();
                ThrowUtil.throwIf(!update, ErrorEnum.SYSTEM_ERROR, "系统错误,额度更新失败");
            }

            return true;
        });
        this.clearPictureFile(picture);
        return true;
    }

    public void doPictureReview(PictureReviewRequest pictureReviewRequest, UserSession userSession) {
        Long id = pictureReviewRequest.getId();
        Integer reviewStatus = pictureReviewRequest.getReviewStatus();
        String reviewMessage = pictureReviewRequest.getReviewMessage();
        PictureReviewStatusEnum reviewStatusEnum = PictureReviewStatusEnum.getEnumByValue(reviewStatus);
        ThrowUtil.throwIf(reviewStatusEnum == null, ErrorEnum.PARAM_EMPTY, "审核参数为空");
        ThrowUtil.throwIf(PictureReviewStatusEnum.REVIEWING.equals(reviewStatusEnum), ErrorEnum.PARAM_CHECK_MISTAKE, "审核状态参数异常");
        Picture oldPicture = this.getById(id);
        ThrowUtil.throwIf(oldPicture == null, ErrorEnum.RESOURCE_NOT_FIND, "当前图片不存在");
        ThrowUtil.throwIf(oldPicture.getReviewStatus().equals(reviewStatus), ErrorEnum.PARAM_CHECK_MISTAKE, "请勿重复提交审核");
        Picture updatePicture = new Picture();
        updatePicture.setId(id);
        updatePicture.setReviewStatus(reviewStatus);
        updatePicture.setReviewerId(userSession.getId());
        updatePicture.setReviewMessage(reviewMessage);
        updatePicture.setReviewTime(new Date());
        boolean updateRes = this.updateById(updatePicture);
        ThrowUtil.throwIf(!updateRes, ErrorEnum.SYSTEM_ERROR, "审核失败");
    }

    @Override
    public List<PictureVo> searchByColor(Long spaceId, String picColor, UserSession userSession) {
        ThrowUtil.throwIf(spaceId < 1, ErrorEnum.PARAM_ILLICIT, "空间id非法");
        ThrowUtil.throwIf(!StringUtils.hasText(picColor), ErrorEnum.PARAM_ILLICIT, "目标颜色非法");
        //校验权限
        Space space = spaceService.getById(spaceId);
        ThrowUtil.throwIf(space == null, ErrorEnum.RESOURCE_NOT_FIND, "当前空间不存在");
        if (!space.getUserId().equals(userSession.getId())) {
            throw new BusinessException(ErrorEnum.NO_AUTH, "没有访问空间权限");
        }
        List<Picture> pictureList = this.lambdaQuery()
                .eq(Picture::getSpaceId, spaceId)
                .isNotNull(Picture::getPicColor)
                .list();

        if (CollUtil.isEmpty(pictureList)) {
            return Collections.emptyList();
        }

        // 计算颜色相似度
        Color color = Color.decode(picColor);
        List<PictureVo> pictureVoList = pictureList.stream()
                .sorted(Comparator.comparingDouble(picture -> {
                    String picColor1 = picture.getPicColor();
                    if (!StringUtils.hasText(picColor1)) {
                        return Double.MAX_VALUE;
                    }
                    Color currentColor = Color.decode(picColor1);
                    return -ColorUtil.calculateSimilarity(color, currentColor);
                }))
                .limit(12)
                .map(PictureVo::objToVo)
                .collect(Collectors.toList());
        return pictureVoList;
    }

    @Override
    public PictureProcessVo produceIdPhoto(MultipartFile multipartFile, PictureProcessRequest pictureProcessRequest,
                                           UserSession userSession) {

        File file = null;
        if (Objects.nonNull(multipartFile)) {
            String originalFilename = multipartFile.getOriginalFilename();
            try {
                file = File.createTempFile(originalFilename, FileUtil.getSuffix(originalFilename));
                multipartFile.transferTo(file);
            } catch (IOException e) {
                log.error("produce id photo fail:{}", e.getMessage());
                throw new BusinessException(ErrorEnum.UPLOAD_FILE_EMPTY, "证件照制作失败");
            }
        }

        IdPhotoProcessDto idPhotoProcessDto = BeanUtil.copyProperties(pictureProcessRequest, IdPhotoProcessDto.class);
        if (Objects.nonNull(file)) {
            idPhotoProcessDto.setInputImage(file);
        }
        // 不是vip 清除vip参数
        if (!userSession.getIsVip()) {
            idPhotoProcessDto.setWhiteningStrength(0);
            idPhotoProcessDto.setBrightnessStrength(0);
            idPhotoProcessDto.setContrastStrength(0);
            idPhotoProcessDto.setSaturationStrength(0);
            idPhotoProcessDto.setSharpenStrength(0);
        }

        ProcessResult processResult = pictureProcessApi.idPhoto(idPhotoProcessDto);
        PictureProcessVo pictureProcessVo = new PictureProcessVo();
        pictureProcessVo.setImage(processResult.getImage_base64_standard());
        pictureProcessVo.setImageHD(processResult.getImage_base64_hd());
        // 登录时可查看高清图
//        if (Objects.nonNull(userSession)) {
//            pictureProcessVo.setImageHD(processResult.getImage_base64_hd());
//        }
        File finalFile = file;
        new Thread(() -> {
            if (Objects.nonNull(finalFile)) {
                try {
                    finalFile.delete();
                } catch (Exception e) {
                    log.error("delete temp image fail");
                }

            }
        }).start();
        return pictureProcessVo;
    }

    @Override
    public PictureProcessDownloadVo downloadIdPhoto(String color, String inputImageBase64, UserSession userSession) {
        PictureProcessDownloadVo pictureProcessDownloadVo = new PictureProcessDownloadVo();
        BackgroundProcessDto backgroundProcessDto = new BackgroundProcessDto();
        backgroundProcessDto.setColor(color);
        backgroundProcessDto.setInputImageBase64(inputImageBase64);
        ProcessResult processResult = pictureProcessApi.addBackgroundColor(backgroundProcessDto);
        // 非vip 加上水印
        if (!userSession.getIsVip()) {
            WatermarkProcessDto watermarkProcessDto = new WatermarkProcessDto();
            watermarkProcessDto.setInputImageBase64(processResult.getImage_base64());
            processResult = pictureProcessApi.watermark(watermarkProcessDto);
        }
        pictureProcessDownloadVo.setImageBase64(processResult.getImage_base64());
        return pictureProcessDownloadVo;
    }

    @Async
    public void clearPictureFile(Picture oldPicture) {
        String pictureUrl = oldPicture.getUrl();
        long count = this.lambdaQuery().eq(Picture::getUrl, pictureUrl).count();
        if (count <= 1L) {
            this.filePictureUpload.deletePicture(pictureUrl);
            String thumbnailUrl = oldPicture.getThumbnailUrl();
            if (StrUtil.isNotBlank(thumbnailUrl)) {
                this.filePictureUpload.deletePicture(thumbnailUrl);
            }

            String compressUrl = oldPicture.getCompressUrl();
            if (StrUtil.isNotBlank(compressUrl)) {
                this.filePictureUpload.deletePicture(compressUrl);
            }

        }
    }

    public Integer uploadPictureByBatch(PictureUploadByBatchRequest pictureUploadByBatchRequest, UserSession userSession) {

        Integer count = pictureUploadByBatchRequest.getCount();
        Integer source = pictureUploadByBatchRequest.getSource();
        String english = PictureCategoryEnum.toEnglish(pictureUploadByBatchRequest.getCategory());
        ThrowUtil.throwIf(english == null, ErrorEnum.PARAM_ILLICIT, "图片分类非法");
        ThrowUtil.throwIf(count > 20, ErrorEnum.PARAM_ILLICIT, "最多 20 条");
        String namePrefix = pictureUploadByBatchRequest.getNamePrefix();
        String searchText = english;

        if (StrUtil.isBlank(namePrefix)) {
            namePrefix = searchText;
        }
        if (source == null) {
            source = 1;
        }
        ObtainImageQueryDto obtainImageQueryDto = new PexelsImageQueryDto();
        obtainImageQueryDto.setSearchKey(searchText);
        obtainImageQueryDto.setPerPage(count);
        List<ObtainImageResult> imageResults = downloadImageFactory.getObtainImage(source).getImageUrls(obtainImageQueryDto);
        log.info("开始下载图片");
        Iterator<ObtainImageResult> iterator = imageResults.iterator();

        ExecutorService executor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
        AtomicInteger uploadCount = new AtomicInteger(0);
        AtomicInteger nameSequence = new AtomicInteger(0);

        while (iterator.hasNext()) {
            ObtainImageResult imageResult = iterator.next();

            String finalNamePrefix = namePrefix;
            executor.execute(() -> {
                PictureUploadRequest request = new PictureUploadRequest();
                // 生成唯一有序的文件名（允许存在间隙）
                request.setPicName(finalNamePrefix + nameSequence.incrementAndGet());
                request.setCategory(pictureUploadByBatchRequest.getCategory());
                request.setTags(pictureUploadByBatchRequest.getTags());
                request.setIntroduction(imageResult.getAlt());

                try {
                    PictureVo pictureVo = uploadPicture(imageResult.getUrl(), request, userSession);
                    log.info("pictureId = {} 上传成功", pictureVo.getId());
                    uploadCount.incrementAndGet();  // 成功计数
                } catch (Exception e) {
                    log.error("url = {} 的图片上传失败", imageResult.getUrl(), e);
                }
            });
        }

        // 关闭线程池并等待所有任务完成
        executor.shutdown();
        try {
            executor.awaitTermination(1, TimeUnit.HOURS);  // 根据实际情况调整超时时间
        } catch (InterruptedException e) {
            log.error("线程池等待被中断", e);
            Thread.currentThread().interrupt();
        }

        log.info("批量上传完成，成功上传 {} 张图片", uploadCount.get());
        return uploadCount.get();
    }

    /**
     * 批量填充额外字段信息
     *
     * @param userSession            当前用户
     * @param waterfallPictureVoList 瀑布流信息
     * @return
     */
    private List<WaterfallPictureVo> fillWaterfallPictureVoField(UserSession userSession, List<WaterfallPictureVo> waterfallPictureVoList) {
        List<WaterfallPictureVo> waterfallPictureVos = waterfallPictureVoList.stream()
                .map(waterfallPictureVo -> fillField(waterfallPictureVo, userSession))
                .collect(Collectors.toList());
        return waterfallPictureVos;
    }

    /**
     * 填充是否点赞字段
     *
     * @param waterfallPictureVo
     * @param userSession
     * @return
     */
    private WaterfallPictureVo fillField(WaterfallPictureVo waterfallPictureVo, UserSession userSession) {
        waterfallPictureVo.setIsFavorite(fillIsFavoriteField(waterfallPictureVo.getId(), userSession));
        return waterfallPictureVo;
    }

    /**
     * 判断当前用户是否点赞当前图片
     *
     * @param pictureId   图片id
     * @param userSession
     * @return
     */
    private boolean fillIsFavoriteField(Long pictureId, UserSession userSession) {
        if (userSession == null || userSession.getId() == null) {
            return false;
        }

        Long userId = userSession.getId();

        return favoritePictureByLuaService.haveFavoritePictureRecord(userId, pictureId);
    }

    private QueryWrapper<Picture> buildQueryWrapper(PicturePageQueryRequest picturePageQueryRequest) {
        QueryWrapper<Picture> queryWrapper = new QueryWrapper();
        Long id = picturePageQueryRequest.getId();
        String url = picturePageQueryRequest.getUrl();
        String picName = picturePageQueryRequest.getPicName();
        String picNameDim = picturePageQueryRequest.getPicNameDim();
        String introduction = picturePageQueryRequest.getIntroduction();
        String introductionDim = picturePageQueryRequest.getIntroductionDim();
        String category = picturePageQueryRequest.getCategory();
        List<String> tags = picturePageQueryRequest.getTags();
        Long picSize = picturePageQueryRequest.getPicSize();
        Integer picWidth = picturePageQueryRequest.getPicWidth();
        Integer picHeight = picturePageQueryRequest.getPicHeight();
        Double picScale = picturePageQueryRequest.getPicScale();
        String picFormat = picturePageQueryRequest.getPicFormat();
        Long userId = picturePageQueryRequest.getUserId();
        String sortField = picturePageQueryRequest.getSortField();
        String sort = picturePageQueryRequest.getSort();
        String searchText = picturePageQueryRequest.getSearchText();
        Integer reviewStatus = picturePageQueryRequest.getReviewStatus();
        Long spaceId = picturePageQueryRequest.getSpaceId();
        Date startEditTime = picturePageQueryRequest.getStartEditTime();
        Date endEditTime = picturePageQueryRequest.getEndEditTime();
        if (StrUtil.isNotBlank(searchText)) {
            queryWrapper.and((qw) -> qw.like("pic_name", searchText).or().like("introduction", searchText));
        }

        if (CollUtil.isNotEmpty(tags)) {
            Iterator var22 = tags.iterator();

            while (var22.hasNext()) {
                String tag = (String) var22.next();
                queryWrapper.like("TAGS", "\"" + tag + "\"");
            }
        }

        queryWrapper.eq(id != null, "id", id)
                .eq(StringUtils.hasText(url), "url", url)
                .eq(StringUtils.hasText(picName), "pic_name", picName)
                .eq(StringUtils.hasText(introduction), "introduction", introduction)
                .eq(StringUtils.hasText(picFormat), "pic_format", picFormat)
                .eq(userId != null, "user_id", userId)
                .eq(picSize != null, "pic_size", picSize)
                .eq(picWidth != null, "pic_width", picWidth)
                .eq(picHeight != null, "pic_height", picHeight)
                .eq(picScale != null, "pic_scale", picScale)
                .eq(spaceId != null, "space_id", spaceId)
                .eq(reviewStatus != null, "review_status", reviewStatus)
                .like(StringUtils.hasText(category), "CATEGORY", category)
                .like(StringUtils.hasText(picNameDim), "pic_name", picName)
                .like(StringUtils.hasText(introductionDim), "introduction", introduction)
                .ge(startEditTime != null, "edit_time", startEditTime)
                .lt(endEditTime != null, "edit_time", endEditTime)
                .orderBy(StringUtils.hasText(sortField), "asc".equals(sort), sortField);
        return queryWrapper;
    }

    private Page<Picture> page(Integer pageNum, Integer pageSize, QueryWrapper<Picture> queryWrapper) {
        if (pageNum == null) {
            pageNum = 1;
        }

        if (pageSize == null) {
            pageSize = 20;
        }

        Page<Picture> page = new Page((long) pageNum, (long) pageSize);
        this.page(page, queryWrapper);
        return page;
    }

    private void checkPictureAuth(UserSession userSession, Picture picture) {
        Long spaceId = picture.getSpaceId();
        if (spaceId == null) {
            boolean condition = userSession.isAdmin() || picture.getUserId().equals(userSession.getId());
            ThrowUtil.throwIf(!condition, ErrorEnum.NO_AUTH, "删除失败，权限不足");
        } else {
            ThrowUtil.throwIf(!picture.getUserId().equals(userSession.getId()), ErrorEnum.NO_AUTH, "删除失败,权限不足");
        }
    }
}
